/*
 * Copyright 2008 Jeff Dwyer
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.apress.progwt.client.calculator;

import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestionEvent;
import com.google.gwt.user.client.ui.SuggestionHandler;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;

public class Calculator extends Composite {

    /**
     * Format is postive;negative. We want to override the default
     * thousands separator ',' to help ease our parser.
     */
    private static final NumberFormat nf = NumberFormat
            .getDecimalFormat().getFormat("###0.#####;-###0.#####");

    private ControlAction lastAction;
    private boolean doClearOnNextDigit;

    private TextBox inputBox;
    private double lastNum = 0;

    private TextArea ticker;

    public Calculator() {

        DockPanel dockPanel = new DockPanel();

        Grid controls = new Grid(5, 2);
        Grid numbersP = new Grid(4, 3);

        // initialize the 1-9 buttons
        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 3; col++) {
                numbersP.setWidget(row, col, new NumberButton(this, row
                        * 3 + col + 1));
            }
        }
        numbersP.setWidget(3, 0, new NumberButton(this, 0));
        numbersP.setWidget(3, 1, new NumberButton(this, "."));
        numbersP.setWidget(3, 2, new ControlButton(this,
                new ControlAction(this, "+/-") {
                    @Override
                    public boolean isMultiArg() {
                        return false;
                    }

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return -1 * current;
                    }
                }));

        controls.setWidget(0, 0, new ControlButton(this,
                new ControlAction(this, "/") {

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return previous / current;
                    }
                }));

        controls.setWidget(0, 1, new ControlButton(this,
                new ControlAction(this, "sqrt") {
                    @Override
                    public boolean isMultiArg() {
                        return false;
                    }

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return Math.sqrt(current);
                    }
                }));

        controls.setWidget(1, 0, new ControlButton(this,
                new ControlAction(this, "*") {
                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return previous * current;
                    }
                }));
        controls.setWidget(1, 1, new ControlButton(this,
                new ControlAction(this, "%") {
                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return previous % current;
                    }
                }));
        controls.setWidget(2, 0, new ControlButton(this,
                new ControlAction(this, "-") {
                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return previous - current;
                    }
                }));
        controls.setWidget(2, 1, new ControlButton(this,
                new ControlAction(this, "1/x") {
                    @Override
                    public boolean isMultiArg() {
                        return false;
                    }

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return 1 / current;
                    }
                }));
        controls.setWidget(3, 0, new ControlButton(this,
                new ControlAction(this, "+") {
                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return previous + current;
                    }
                }));
        controls.setWidget(3, 1, new ControlButton(this,
                new ControlAction(this, "=") {
                    @Override
                    public boolean isMultiArg() {
                        return false;
                    }

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        if (lastAction == null) {
                            return current;
                        }
                        return lastAction.performAction(null, previous,
                                current);
                    }

                }));

        controls.setWidget(4, 0, new ControlButton(this,
                new ControlAction(this, "bksp") {
                    @Override
                    public boolean isMultiArg() {
                        return false;
                    }

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        String cStr = current + "";
                        if (cStr.endsWith(".0")) {
                            cStr = cStr.substring(0, cStr.length() - 3);
                        } else {
                            cStr = cStr.substring(0, cStr.length() - 1);
                        }
                        if (cStr.equals("")) {
                            cStr = "0";
                        }
                        return Double.parseDouble(cStr);
                    }

                }));

        controls.setWidget(4, 1, new ControlButton(this,
                new ControlAction(this, "clear") {
                    @Override
                    public boolean isMultiArg() {
                        return false;
                    }

                    @Override
                    public double performAction(ControlAction lastAction,
                            double previous, double current) {
                        return 0;
                    }
                }));

        dockPanel.add(numbersP, DockPanel.CENTER);
        dockPanel.add(controls, DockPanel.EAST);

        inputBox = new TextBox();
        inputBox.addStyleName("ResultBox");
        dockPanel.add(inputBox, DockPanel.NORTH);

        ticker = new TextArea();
        ticker.setSize("7em", "140px");

        MultiWordSuggestOracle oracle = new MultiWordSuggestOracle();
        oracle.add("Jill");
        oracle.add("Jeff");
        oracle.add("James");
        oracle.add("Jennifer");

        SuggestBox box = new SuggestBox(oracle);
        box.addEventHandler(new SuggestionHandler() {
            public void onSuggestionSelected(SuggestionEvent suggE) {
                String selected = suggE.getSelectedSuggestion()
                        .getReplacementString();
                // do something with selected suggestion
            }
        });

        dockPanel.add(box, DockPanel.SOUTH);

        HorizontalPanel mainP = new HorizontalPanel();
        mainP.add(dockPanel);
        mainP.add(ticker);

        initWidget(mainP);

        setResult(0);

    }

    public void actionClick(ControlAction action) {
        if (action.isMultiArg()) {
            lastNum = getCurrentNum();
            setDoClearOnNextDigit(true);
            this.lastAction = action;

        } else {
            setResult(action.performAction(lastAction, getLastNum(),
                    getCurrentNum()));
        }
    }

    public void digitAction(String value) {
        if (isDoClearOnNextDigit()) {
            inputBox.setText("");
            setDoClearOnNextDigit(false);
        }
        inputBox.setText(inputBox.getText() + value);
    }

    private double getCurrentNum() {
        return Double.parseDouble(inputBox.getText());
    }

    private double getLastNum() {
        return lastNum;
    }

    public boolean isDoClearOnNextDigit() {
        return doClearOnNextDigit;
    }

    public void setDoClearOnNextDigit(boolean doClearOnNextDigit) {
        this.doClearOnNextDigit = doClearOnNextDigit;
    }

    private void setResult(double res) {
        inputBox.setText(nf.format(res));

        ticker.setText(res + "\n" + ticker.getText());
        GUIEffects.highlight(inputBox);

        if (res == 0) {
            setDoClearOnNextDigit(true);
        }
    }

}
